﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace AutoTime
{
    internal static class Program
    {
        private static int step = 1;
        private static string mIP = "";
        private static DateTime dt;
        private static string tstr = "";
        private static Timer timer = new Timer();
        private static string thost = "10.60.3.250";

        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            bool runone;
            System.Threading.Mutex run = new System.Threading.Mutex(true, "Single_AutoTime", out runone);
            if (runone)
            {
                string[] cmdLine = System.Environment.GetCommandLineArgs();
                if (cmdLine.Count() > 1)
                {
                    thost = cmdLine[1];
                    string tstr=thost.Replace("-", "").ToLower();
                    switch (tstr)
                    {
                        case "u":
                        case "del":
                        case "delete":
                        case "uninstall":
                        case "remove":
                        case "clear":
                            regSetAutoRun(false);
                            Environment.Exit(0);
                            break;
                        case "i":
                        case "install":
                        case "setup":
                        case "add":
                        case "new":
                            regSetAutoRun(true);
                            Environment.Exit(0);
                            break;
                        default:
                            break;
                    }

                }
                timer.Interval = 5000;
                timer.Tick += Timer_Tick;
                timer.Start();
            }
            else
            {
                Environment.Exit(1);
            }
            Application.Run();
        }

        private static void Timer_Tick(object sender, EventArgs e)
        {
            if (step == 1)
            {
                if (string.IsNullOrWhiteSpace(mIP) || mIP.Equals("127.0.0.1"))
                {
                    mIP = getMashineIP();
                }
                else
                {
                    step = 2;
                }
            }
            if (step == 2)
            {
                if (DateTimeSynchronization.Synchronization(thost, out dt, out tstr))
                {
                    timer.Stop();
                    TimeSpan dayspan = new TimeSpan(DateTime.Now.Ticks - dt.Ticks);
                    if (Math.Floor(dayspan.TotalHours) != 0)
                    {
                        bool re = DateTimeSynchronization.SetLocalDateTime(dt);
                    }
                    Environment.Exit(0);
                }
            }
        }

        private static string getMashineIP()
        {
            string mashineIP = "";
            string HostName = System.Net.Dns.GetHostName(); //得到主机名
            System.Net.IPHostEntry IpEntry = System.Net.Dns.GetHostEntry(HostName);
            for (int i = 0; i < IpEntry.AddressList.Length; i++)
            {
                //从IP地址列表中筛选出IPv4类型的IP地址
                //AddressFamily.InterNetwork表示此IP为IPv4,
                //AddressFamily.InterNetworkV6表示此地址为IPv6类型
                if (IpEntry.AddressList[i].AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    mashineIP = IpEntry.AddressList[i].ToString();
                    break;
                }
            }
            return mashineIP;
        }
        /// <summary>
        /// 设置应用为自启动模式
        /// </summary>
        /// <param name="isrun">设置程序是否自启动</param>
        public static void regSetAutoRun(bool isrun)
        {
            string substr = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
            Microsoft.Win32.RegistryKey rootkey = Microsoft.Win32.Registry.LocalMachine;
            Microsoft.Win32.RegistryKey prockey = rootkey.OpenSubKey(substr, true);
            if (isrun)
            {
                string fn = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
                prockey.SetValue("AutoTime.exe", fn, Microsoft.Win32.RegistryValueKind.String);
            }
            else
            {
                prockey.DeleteValue("AutoTime.exe", false);
            }
            prockey.Close();
            rootkey.Close();
        }
        /// <summary>
        /// 获取程序自启动状态
        /// </summary>
        /// <returns></returns>
        public static bool regGetAutoRun()
        {
            bool isrun = false;
            string substr = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
            Microsoft.Win32.RegistryKey rootkey = Microsoft.Win32.Registry.LocalMachine;
            Microsoft.Win32.RegistryKey prockey = rootkey.OpenSubKey(substr);
            string[] subnames = prockey.GetValueNames();
            foreach (string str in subnames)
            {
                if (str.Equals("AutoTime.exe", System.StringComparison.OrdinalIgnoreCase))
                {
                    isrun = true;
                    break;
                }
            }
            prockey.Close();
            rootkey.Close();
            return isrun;
        }

    }

    internal class DateTimeSynchronization
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct Systemtime
        {
            public short year;
            public short month;
            public short dayOfWeek;
            public short day;
            public short hour;
            public short minute;
            public short second;
            public short milliseconds;
        }

        [DllImport("kernel32.dll")]
        private static extern bool SetLocalTime(ref Systemtime time);
        [DllImport("Kernel32.dll")]
        private static extern void GetLocalTime(ref Systemtime Time);

        private static uint swapEndian(ulong x)
        {
            return (uint)(((x & 0x000000ff) << 24) +
            ((x & 0x0000ff00) << 8) +
            ((x & 0x00ff0000) >> 8) +
            ((x & 0xff000000) >> 24));
        }

        /// <summary>
        /// 设置系统时间
        /// </summary>
        /// <param name="dt">需要设置的时间</param>
        /// <returns>返回系统时间设置状态，true为成功，false为失败</returns>
        public static bool SetLocalDateTime(DateTime dt)
        {
            Systemtime st;
            st.year = (short)dt.Year;
            st.month = (short)dt.Month;
            st.dayOfWeek = (short)dt.DayOfWeek;
            st.day = (short)dt.Day;
            st.hour = (short)dt.Hour;
            st.minute = (short)dt.Minute;
            st.second = (short)dt.Second;
            st.milliseconds = (short)dt.Millisecond;
            bool rt = SetLocalTime(ref st);
            return rt;
        }
        private static IPAddress iPAddress = null;
        public static bool Synchronization(string host, out DateTime syncDateTime, out string message)
        {
            syncDateTime = DateTime.Now;
            try
            {
                message = "";
                if (iPAddress == null)
                {
                    if (System.Text.RegularExpressions.Regex.IsMatch(host, @"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"))
                    {
                        iPAddress = IPAddress.Parse(host);
                    }
                    else
                    {
                        var iphostinfo = Dns.GetHostEntry(host);
                        var ntpServer = iphostinfo.AddressList[0];
                        iPAddress = ntpServer;
                    }
                }
                DateTime dtStart = DateTime.Now;
                //NTP消息大小摘要是16字节 (RFC 2030)
                byte[] ntpData = new byte[48];
                //设置跳跃指示器、版本号和模式值
                // LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
                ntpData[0] = 0x1B;
                IPAddress ip = iPAddress;
                // NTP服务给UDP分配的端口号是123
                IPEndPoint ipEndPoint = new IPEndPoint(ip, 123);
                // 使用UTP进行通讯
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                socket.Connect(ipEndPoint);
                socket.ReceiveTimeout = 3000;
                socket.Send(ntpData);
                socket.Receive(ntpData);
                socket.Close();
                socket.Dispose();
                DateTime dtEnd = DateTime.Now;
                //传输时间戳字段偏移量，以64位时间戳格式，应答离开客户端服务器的时间
                const byte serverReplyTime = 40;
                // 获得秒的部分
                ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
                //获取秒的部分
                ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
                //由big-endian 到 little-endian的转换
                intPart = swapEndian(intPart);
                fractPart = swapEndian(fractPart);
                ulong milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000UL);
                // UTC时间
                DateTime webTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds(milliseconds);
                //本地时间
                DateTime dt = webTime.ToLocalTime();
                //bool isSuccess = SetLocalDateTime(dt);
                syncDateTime = dt;

            }
            catch (Exception ex)
            {
                message = ex.Message;
                return false;
            }
            return true;

        }
    }
}
